use std::future::{ready, Ready};

use actix_web::body::EitherBody;
use actix_web::dev::{self, ServiceRequest, ServiceResponse};
use actix_web::dev::{Service, Transform};
use actix_web::{Error, HttpResponse};
use chrono::Local;
use futures_util::future::LocalBoxFuture;
use crate::{models, session, SessionStore};


pub struct CheckLogin {
    session: session::MemorySession,
}

impl CheckLogin {
    pub(crate) fn set_session(session: session::MemorySession) -> Self {
        CheckLogin { session }
    }
}

impl<S, B> Transform<S, ServiceRequest> for CheckLogin
    where
        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
        S::Future: 'static,
        B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Transform = CheckLoginMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(CheckLoginMiddleware { service, session: self.session.clone() }))
    }
}
pub struct CheckLoginMiddleware<S> {
    service: S,
    session: session::MemorySession,
}

impl<S, B> Service<ServiceRequest> for CheckLoginMiddleware<S>
    where
        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
        S::Future: 'static,
        B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    dev::forward_ready!(service);

    fn call(&self, request: ServiceRequest) -> Self::Future {
        if request.path().starts_with("/api/") && request.path() != "/api/login/account" {
            let response = HttpResponse::Unauthorized().json(models::SysResult {
                success: true,
                error_code: "401".to_string(),
                error_message: "请先登录！".to_string(),
                data: Some(models::LoginStatus {
                    is_login: false,
                })
            }).map_into_right_body();
            let header_option = request.headers().get("Access-Token");
            //request.query_string()
            if header_option.is_none() {
                let (request, _pl) = request.into_parts();
                return Box::pin(async { Ok(ServiceResponse::new(request, response)) });
            }
            let header_value_result = header_option.unwrap().to_str();
            if header_value_result.is_err() {
                let (request, _pl) = request.into_parts();
                return Box::pin(async { Ok(ServiceResponse::new(request, response)) });
            }
            let header_value = header_value_result.unwrap();
            let session_value = self.session.get(header_value);
            if session_value.is_none() {
                let (request, _pl) = request.into_parts();
                return Box::pin(async { Ok(ServiceResponse::new(request, response)) });
            }
            let mut data = self.session.session.lock().unwrap();
            data.insert(header_value.to_string(), Local::now().timestamp_millis());
        }

        let res = self.service.call(request);

        Box::pin(async move {
            // forwarded responses map to "left" body
            res.await.map(ServiceResponse::map_into_left_body)
        })
    }
}